Skip to content

[WIP] Refactor Studio layout to match Supabase style#1204

Merged
hotlong merged 5 commits intomainfrom
claude/restructure-studio-layout
Apr 21, 2026
Merged

[WIP] Refactor Studio layout to match Supabase style#1204
hotlong merged 5 commits intomainfrom
claude/restructure-studio-layout

Conversation

@Claude
Copy link
Copy Markdown
Contributor

@Claude Claude AI commented Apr 21, 2026

Thanks for asking me to work on this. I will get started on it and keep this PR's description up to date as I form a plan and make progress.


This section details on the original issue you should resolve

<issue_title>Studio 布局重构:Supabase 风格的 TopBar + LeftSidebar + Content</issue_title>
<issue_description>

Context

当前 Studio 的布局是 Shadcn SidebarProvider + 左侧边栏(GlobalSidebarAppSidebar)+ 每个页面内部再自行 <SiteHeader /> 的模式。问题:

  1. Org / Env / Package 切换分散在 SiteHeader(Org、Env)和 AppSidebar 头部(Package),同一认知层级被切成两处。
  2. 每个路由都要手动 import SiteHeader,重复且容易漂移(breadcrumb 逻辑、操作按钮样式都不一致)。
  3. 视觉上缺少 Supabase 那种"顶部全局 chrome + 左侧功能导航 + 右侧纯内容区"的清晰三段式。

目标:改造为 顶部栏(全局 chrome)+ 左侧栏(保留 metadata 树)+ 右侧内容区,让全局身份上下文集中在顶部,页面只关注 body。

Image

方案

1. 新增 TopBar 全局组件

文件:apps/studio/src/components/top-bar.tsx(新建)

承载:

  • 左段:Logo + OrganizationSwitcher(复用 components/organization-switcher.tsx)+ EnvironmentSwitcher(复用 components/environment-switcher.tsx)+ Package 切换器(从 app-sidebar.tsx 里 SidebarHeader 的 DropdownMenu 抽出,命名为 PackageSwitcher,放到 components/package-switcher.tsx
  • 中段:SidebarTrigger(折叠左栏)+ 面包屑(吸收 SiteHeaderviewLabels / selectedMeta / API 路径 Badge 逻辑;通过读取 useLocation + useParams 动态计算,不再需要每页传 props)
  • 右段:全局搜索框(⌘K 占位,后续接入;暂用 shadcn Input + kbd 样式)、环境模式 Badge(config.mode)、ThemeToggleUserMenu

高度仍为 h-12border-b,与现有 SiteHeader 对齐以减少视觉跳变。

2. 改造 routes/__root.tsx

  • RootComponentSidebarProvider 内部、<Outlet /> 外层加一个 flex 列容器:<TopBar /> 在上,下方是 <div className="flex flex-1 min-h-0">{sidebar}{outlet}</div>
  • TopBar 仅在已登录且非 public 路由时渲染(沿用 RequireAuth 的逻辑)。
  • GlobalSidebar / AppSidebar 的选择逻辑保留(isGlobalShellPath),但它们从此只负责"功能导航 + metadata 树",不再承担 Org/Env/Package 切换。
    • global-sidebar.tsx:删除 OrgHeader(Org 切换器移到 TopBar),SidebarHeader 可直接省略或仅留 Logo。
    • app-sidebar.tsx:删除顶部 Package Switcher SidebarHeader 块,把 Package 下拉菜单抽到新的 PackageSwitcher 组件给 TopBar 使用。

3. 删除每页 <SiteHeader />

影响文件(共 11 个路由):

  • routes/index.tsx
  • routes/api-console.tsx
  • routes/orgs.index.tsx / orgs.new.tsx / orgs.$orgId.tsx
  • routes/environments.index.tsx / environments.$environmentId.index.tsx / environments.$environmentId.packages.tsx
  • routes/environments.$environmentId.$package.index.tsx
  • routes/environments.$environmentId.$package.objects.$name.tsx
  • routes/environments.$environmentId.$package.metadata.$type.$name.tsx

每个文件:

  • 删除 import { SiteHeader } ...<SiteHeader ... />
  • 页面顶层容器 <div className="flex h-svh flex-col ..."> 去掉 h-svh(高度交由根布局的 flex container 接管),或者改成 flex-1 min-h-0 overflow-auto
  • SiteHeader 里传入的 selectedObject / selectedMeta / selectedView 信息由 TopBar 通过路由参数自行推断,不再需要页面显式传值。

4. 删除 site-header.tsx

所有用处被 TopBar 吸收后,删除 apps/studio/src/components/site-header.tsx

5. environments.$environmentId.$package.tsx 调整

当前 <main className="flex min-w-0 flex-1 flex-col h-svh overflow-hidden ..."> 里的 h-svh 会和新的根 flex 冲突,改为 flex min-w-0 flex-1 flex-col overflow-hidden

关键文件

路径 操作
apps/studio/src/components/top-bar.tsx 新建
apps/studio/src/components/package-switcher.tsx 新建(从 app-sidebar 抽出)
apps/studio/src/routes/__root.tsx 加入 TopBar + flex 列布局
apps/studio/src/components/global-sidebar.tsx 删除 OrgHeader
apps/studio/src/components/app-sidebar.tsx 删除顶部 Package Switcher header
apps/studio/src/components/site-header.tsx 删除
上述 11 个路由文件 移除 <SiteHeader /> 引用 & 调整外层高度 class

可复用的既有资产

  • components/organization-switcher.tsxcomponents/environment-switcher.tsxcomponents/environment-badge.tsxcomponents/theme-toggle.tsxcomponents/user-menu.tsx 全部直接复用。
  • Shadcn SidebarProvider / SidebarTrigger 保留,继续提供折叠能力。
  • hooks/useSessionuseEnvironmentDetailuseEnvAwarePackages 已提供 TopBar 需要的所有上下文。
  • 面包屑渲染可复用 components/ui/breadcrumb.tsx

验证

  1. pnpm --filter @objectstack/studio dev 启动,依次访问:
    • /login → 无 TopBar / Sidebar(public 路由)。
    • /environments → TopBar 显示 Org 切换器 + Env 切换器(Env 未选时 disabled);左侧 GlobalSidebar 仅剩功能入口,不再有 Org 下拉。
    • /environments/:id → Env 切换器显示当前 env;左侧 GlobalSidebar;无页面级二级 header。
    • /environments/:id/:package/objects/:name → TopBar 出现 Package Switcher + 面包屑 Package › Object › <name> + API 路径 Badge;左侧 AppSidebar 的 metadata 树正常展开,顶部不再重复 Package 下拉。
  2. 折叠 SidebarTrigger,右侧内容区无抖动。
  3. 切换主题、切换 Org、切换 Env、切换 Package 全部通过 TopBar 触发,并正确更新 URL / session。
  4. pnpm --filter @objectstack/studio test 通过;全量 pnpm build 通过(TypeScript 校验会捕获未清理的 <SiteHeader /> 引用)。
    </issue_description>

Comments on the Issue (you are @claude[agent] in this section)

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectstack-demo Ready Ready Preview, Comment Apr 21, 2026 6:39am
spec Ready Ready Preview, Comment Apr 21, 2026 6:39am

Request Review

- Create PackageSwitcher component extracted from app-sidebar
- Create TopBar component with global chrome (Org/Env/Package switchers, breadcrumbs, search, mode badge)
- Update __root.tsx to integrate TopBar in flex column layout
- Remove OrgHeader from global-sidebar (moved to TopBar)
- Remove Package Switcher header from app-sidebar (moved to TopBar)

Agent-Logs-Url: https://github.com/objectstack-ai/framework/sessions/095ed50f-741b-4562-b7f1-f4cadbe5b7b1

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
- Remove SiteHeader imports and usage from all 11 route files
- Adjust container height classes (remove h-svh, use flex-1)
- Delete site-header.tsx component (replaced by TopBar)

Agent-Logs-Url: https://github.com/objectstack-ai/framework/sessions/095ed50f-741b-4562-b7f1-f4cadbe5b7b1

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
- Remove SidebarTrigger from TopBar (was in header)
- Add SidebarFooter with SidebarTrigger to both GlobalSidebar and AppSidebar
- Move SidebarProvider from root wrapper to RequireAuth for proper layout control
- Change SidebarProvider height from h-svh to min-h-0 for flexible full-width layout
- Update TopBar to remove SidebarTrigger import and usage

Agent-Logs-Url: https://github.com/objectstack-ai/framework/sessions/cca4add0-2ad9-4a22-964c-49d4c9e4eb36

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
@hotlong hotlong marked this pull request as ready for review April 21, 2026 07:02
Copilot AI review requested due to automatic review settings April 21, 2026 07:02
@hotlong hotlong merged commit 6f4d54f into main Apr 21, 2026
13 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors Studio’s authenticated layout toward a Supabase-style “TopBar + Sidebar + Content” shell, centralizing org/env/package context in a global top chrome and removing per-page SiteHeader usage.

Changes:

  • Added a new TopBar (with breadcrumbs, global controls, and optional PackageSwitcher) and wired it into the authenticated root layout.
  • Removed SiteHeader and updated multiple routes to rely on the new root flex/scroll container structure.
  • Simplified GlobalSidebar / AppSidebar headers (moving switching UI out) and adjusted sidebar layout behavior.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
apps/studio/src/routes/__root.tsx Introduces the new authenticated shell structure (TopBar + sidebar/content) and package syncing logic for the TopBar.
apps/studio/src/components/top-bar.tsx New global top chrome component with URL-inferred breadcrumbs and an API path badge.
apps/studio/src/components/package-switcher.tsx New extracted dropdown component for switching installed packages (used by TopBar).
apps/studio/src/components/site-header.tsx Removes the old per-page header component entirely.
apps/studio/src/components/global-sidebar.tsx Removes org switcher header and focuses sidebar on navigation; adds footer trigger.
apps/studio/src/components/app-sidebar.tsx Removes the package switcher header UI and adds footer trigger (metadata tree remains).
apps/studio/src/components/ui/sidebar.tsx Adjusts SidebarProvider wrapper sizing classes to work with the new layout.
apps/studio/src/routes/index.tsx Removes SiteHeader and updates height/overflow handling to fit the new root layout.
apps/studio/src/routes/api-console.tsx Removes SiteHeader and relies on the new root chrome.
apps/studio/src/routes/orgs.index.tsx Removes SiteHeader and updates scroll container classes.
apps/studio/src/routes/orgs.new.tsx Removes SiteHeader and updates scroll container classes.
apps/studio/src/routes/orgs.$orgId.tsx Removes SiteHeader and updates scroll container classes.
apps/studio/src/routes/environments.index.tsx Removes SiteHeader and updates layout sizing to integrate with root shell.
apps/studio/src/routes/environments.$environmentId.index.tsx Removes SiteHeader and updates layout sizing to integrate with root shell.
apps/studio/src/routes/environments.$environmentId.packages.tsx Removes SiteHeader and updates layout sizing to integrate with root shell.
apps/studio/src/routes/environments.$environmentId.$package.tsx Removes h-svh to avoid height conflicts under the new root flex layout.
apps/studio/src/routes/environments.$environmentId.$package.index.tsx Removes SiteHeader and keeps content as a scroll-managed flex child.
apps/studio/src/routes/environments.$environmentId.$package.objects.$name.tsx Removes SiteHeader and lets the root TopBar handle context/breadcrumbs.
apps/studio/src/routes/environments.$environmentId.$package.metadata.$type.$name.tsx Removes SiteHeader and lets the root TopBar handle context/breadcrumbs.

Comment on lines +203 to +207
{item.href ? (
<BreadcrumbLink href={item.href}>{item.label}</BreadcrumbLink>
) : (
<BreadcrumbPage className="font-medium">{item.label}</BreadcrumbPage>
)}
Comment on lines +137 to +156
// Authenticated layout with TopBar + Sidebar + Content
if (user) {
const showGlobalSidebar = isGlobalShellPath(location.pathname);

return (
<>
<GlobalSidebar />
{children}
</>
<SidebarProvider>
<div className="flex min-h-screen w-full flex-col">
<TopBar
packages={packages}
selectedPackage={selectedPackage}
onSelectPackage={handleSelectPackage}
/>
<div className="flex flex-1 w-full overflow-hidden">
{showGlobalSidebar && <GlobalSidebar />}
<main className="flex flex-1 min-w-0 overflow-hidden">
{children}
</main>
</div>
</div>
</SidebarProvider>
}
className={cn(
"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex h-svh w-full overflow-hidden",
"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-0 w-full",
* Global chrome component in Supabase style.
* Renders at the top of authenticated Studio layouts with:
* - Left segment: OrganizationSwitcher + EnvironmentSwitcher + PackageSwitcher
* - Center segment: SidebarTrigger + dynamic breadcrumbs (inferred from URL)
Comment on lines +300 to 301
// Package switcher state (no longer used in AppSidebar, moved to TopBar)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Studio 布局重构:Supabase 风格的 TopBar + LeftSidebar + Content

3 participants